package com.example.czy.utils;

import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日期处理工具
 *
 * @Author czy
 * @Date 2023-12-27 12:45:20
 **/
public class DateUtil {
    /**
     * 标准年月日时分秒时间
     */
    public final static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";


    /**
     *年月日时分秒时间
     */
    public final static String YYYYMMDD_HH_MM_SS = "yyyyMMdd HH:mm:ss";


    /**
     * 标准年月日
     */
    public final static String YYYY_MM_DD = "yyyy-MM-dd";

    /**
     * 标准年月
     */
    public final static String YYYY_MM = "yyyy-MM";

    /**
     * 年月
     */
    public final static String YYYYMM = "yyyyMM";


    /**
     * 标准时间
     */
    public final static String HH_MM_SS = "HH:mm:ss";


    private final static String TIME_ERROR = "时间格式不支持！";


    private final static String ORDER_ERROR = "开始时间不能大于结束时间！";


    /**
     * 验证日期
     *
     * @param date yyyy-MM-DD
     * @return true|false
     */
    public static boolean validateDate(String date) {
        if (!StringUtils.hasText(date)) {
            return false;
        }

        String dateRegex = "(20)\\d\\d-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])";
        Pattern pattern = Pattern.compile(dateRegex);
        Matcher matcher = pattern.matcher(date);
        return matcher.matches();
    }

    /**
     * 验证日期（多种类型）
     *
     * @param date yyyy-MM-DD|yyyy/MM/dd
     * @return true|false
     */
    public static boolean validateDateMulti(String date) {
        if (!StringUtils.hasText(date)) {
            return false;
        }

        String dateRegex = "(20)\\d\\d-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])|(20)\\d\\d/(0[1-9]|1[0-2])/(0[1-9]|[12]\\d|3[01])";
        Pattern pattern = Pattern.compile(dateRegex);
        Matcher matcher = pattern.matcher(date);
        return matcher.matches();
    }

    /**
     * 验证日期
     *
     * @param date yyyy-MM
     * @return true|false
     */
    public static boolean validateDateYM(String date) {
        if (!StringUtils.hasText(date)) {
            return false;
        }

        String dateRegex = "\\d{4}-\\d{2}";
        Pattern pattern = Pattern.compile(dateRegex);
        Matcher matcher = pattern.matcher(date);
        return matcher.matches();
    }

    /**
     * 验证时间
     *
     * @param time HH:mm:ss
     * @return true|false
     */

    public static boolean validateTime(String time) {
        if (!StringUtils.hasText(time)) {
            return false;
        }
        String timeRegex = "(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]";
        Pattern pattern = Pattern.compile(timeRegex);
        Matcher matcher = pattern.matcher(time);
        return matcher.matches();
    }


    /**
     * 验证时间
     *
     * @param dateTime yyyy-MM-dd HH:mm:ss
     * @return true|false
     */

    public static boolean validateDateTime(String dateTime) {
        if (!StringUtils.hasText(dateTime)) {
            return false;
        }
        String timeRegex = "(\\d{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])\\s([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])";
        Pattern pattern = Pattern.compile(timeRegex);
        Matcher matcher = pattern.matcher(dateTime);
        return matcher.matches();
    }


    /**
     * 将字符串转换为时间
     *
     * @param time    时间字符串
     * @param pattern 时间字符串 yyyy-MM-dd HH:mm:ss|yyyy-MM-dd|HH:mm:ss
     * @param query   LocalDateTime::from|LocalDate::from|LocalTime::from
     * @param <T>
     * @return LocalDateTime|LocalDate|LocalTime
     */
    public static <T> T parse(String time, String pattern, TemporalQuery<T> query) {
        try {
            TemporalAccessor temporal = DateTimeFormatter.ofPattern(pattern).parse(time);
            return query.queryFrom(temporal);
        } catch (DateTimeParseException e) {
            throw new IllegalArgumentException("无法解析输入的时间字符串：" + e.getMessage(), e);
        }
    }



    /**
     * 将字符串转换为时间
     *
     * @param time  时间字符串 yyyy-MM-dd HH:mm:ss|yyyy-MM-dd|HH:mm:ss
     * @param query LocalDate::from|LocalDateTime::from|LocalTime::from
     * @param <T>
     * @return LocalDateTime|LocalDate|LocalTime
     */
    public static <T> T parse(String time, TemporalQuery<T> query) {
        String pattern;
        if (validateDateTime(time)) {
            pattern = YYYY_MM_DD_HH_MM_SS;
        } else if (validateDate(time)) {
            pattern = YYYY_MM_DD;
        } else if (validateTime(time)) {
            pattern = HH_MM_SS;
        }  else {
            throw new RuntimeException(TIME_ERROR);
        }

        try {
            return query.queryFrom(DateTimeFormatter.ofPattern(pattern).parse(time));
        } catch (DateTimeParseException e) {
            throw new IllegalArgumentException(TIME_ERROR + e.getMessage(), e);
        }
    }

    /**
     * 解析日期
     *
     * @param date    yyyy-MM-dd|yyyy-MM
     * @param isStart isStart = true : 开始日期 获得当月第一天|isStart = false : 结束日期 获得当月最后一天
     * @return LocalDate
     */
    public static LocalDate getDateFull(String date, boolean isStart) {
        if (validateDateYM(date)) {
            return parseDateYM(date, isStart);
        }
        return LocalDate.parse(date, DateTimeFormatter.ofPattern(YYYY_MM_DD));
    }


    /**
     * 解析日期
     *
     * @param date yyyy-MM-dd|yyyy-MM
     * @return LocalDate yyyy-MM->默认当月第一天
     */
    public static LocalDate getDateFull(String date) {
        return getDateFull(date, true);
    }

    /**
     * 解析日期
     *
     * @param date    yyyy-MM
     * @param isStart 开始标记 true|false
     * @return 开始带当月第一天，结束带当月最后一天
     */
    private static LocalDate parseDateYM(String date, boolean isStart) {
        if (validateDateYM(date)) {
            if (isStart) {
                return YearMonth.parse(date, DateTimeFormatter.ofPattern(YYYY_MM)).atDay(1);
            }
            return YearMonth.parse(date, DateTimeFormatter.ofPattern(YYYY_MM)).atEndOfMonth();
        } else {
            throw new RuntimeException(TIME_ERROR);
        }
    }


    /**
     * 格式化时间至字符串
     *
     * @param dateTime LocalDateTime|LocalDate|LocalTime
     * @return yyyy-MM-dd HH:mm:ss|yyyy-MM-dd|HH:mm:ss
     */
    public static String format(TemporalAccessor dateTime) {
        String pattern;
        if (dateTime instanceof LocalDateTime) {
            pattern = YYYY_MM_DD_HH_MM_SS;
        } else if (dateTime instanceof LocalDate) {
            pattern = YYYY_MM_DD;
        } else if (dateTime instanceof LocalTime) {
            pattern = HH_MM_SS;
        } else {
            throw new RuntimeException(TIME_ERROR);
        }
        return DateTimeFormatter.ofPattern(pattern).format(dateTime);
    }

    /**
     * 格式化时间至字符串
     *
     * @param dateTime
     * @param formatter String 自定义格式
     * @return String 自定义格式
     */
    public static String format(TemporalAccessor dateTime, String formatter) {
        return DateTimeFormatter.ofPattern(formatter).format(dateTime);
    }

    /**
     * 根据给定的开始和结束日期字符串计算对应的环比日期范围
     * 环比一般指本期与上期（月、周）比较
     *
     * @param startDateStr 当前时间段开始日期字符串，格式为"yyyy-MM-dd"、"yyyy-MM"
     * @param endDateStr   当前时间段结束日期字符串，格式为"yyyy-MM-dd"、"yyyy-MM"
     * @return 环比日期范围数组，其中第一个元素是环比时间段开始日期字符串，第二个元素是环比时间段结束日期字符串
     * @throws Exception 如果输入格式不正确或处理过程中发生错误
     */
    public static String[] getQoqRange(String startDateStr, String endDateStr) {
        LocalDate startDate = getDateFull(startDateStr);
        LocalDate endDate = getDateFull(endDateStr, false);

        if (startDate.isAfter(getDateFull(endDateStr))) {
            throw new RuntimeException(ORDER_ERROR);
        }
        // 环比开始时间
        LocalDate prevStartDate;

        // 计算当前时间段的月数跨度
        long monthsBetween = ChronoUnit.MONTHS.between(startDate, endDate);

        // 计算当前时间段的天数跨度
        long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);

        // 环比结束时间
        LocalDate prevEndDate = startDate.minusDays(1);

        if (monthsBetween != 0) {
            prevStartDate = prevEndDate.minusMonths(monthsBetween).withDayOfMonth(startDate.getDayOfMonth());
        } else {
            prevStartDate = prevEndDate.minusDays(daysBetween);
        }

        return new String[]{format(prevStartDate), format(prevEndDate)};
    }


    /**
     * 根据给定的开始和结束日期字符串计算对应的同比日期范围。
     * 同比一般指本年与上年相比较
     *
     * @param startTime 当前时间段开始日期字符串，格式为"yyyy-MM-dd"、"yyyy-MM"
     * @param endTime   当前时间段结束日期字符串，格式为"yyyy-MM-dd"、"yyyy-MM"
     * @return 环比日期范围数组，其中第一个元素是同比时间段开始日期字符串，第二个元素是同比时间段结束日期字符串
     * @throws Exception 如果输入格式不正确或处理过程中发生错误
     */
    public static String[] getYoyRange(String startTime, String endTime) {
        LocalDate startDate = getDateFull(startTime);
        LocalDate endDate = getDateFull(endTime, false);

        if (startDate.isAfter(getDateFull(endTime))) {
            throw new RuntimeException(ORDER_ERROR);
        }

        LocalDate prevStartDate = startDate.minusYears(1);
        LocalDate prevEndDate = endDate.minusYears(1);
        return new String[]{format(prevStartDate), format(prevEndDate)};
    }


}


